<?php
class Pageley_Controller_Dispatcher_Basic implements Pageley_Controller_Dispatcher {
    private $controllers;
    private $filters;
    private $notFoundHandler;
    private $errorHandler;
    private $viewResolver;
    private $errorView = "error";
    private $application;

    public function __construct($viewResolver = null) {
        $this->controllers = new Pageley_Util_ArrayMap();
        $this->filters = new Pageley_Util_ArrayMap();
        if($viewResolver === null) {
            $viewResolver = new Pageley_Controller_ViewResolver_NoView();
        }
        $this->viewResolver = $viewResolver;
    }
    
    public function setApplication($application) {
        $this->application = $application;
    }

    public function setControllers($controllers) {
        $this->controllers = $controllers;
    }

    public function setFilters($filters) {
        $this->filters = $filters;
    }

    public function setViewResolver($viewResolver) {
        $this->viewResolver = $viewResolver;
    }
    
    protected function getViewResolver() {
        return $this->viewResolver;
    }

    public function setNotFoundHandler($notFoundHandler) {
        $this->notFoundHandler = $notFoundHandler;
    }

    public function setErrorHandler($errorHandler) {
        $this->errorHandler = $errorHandler;
    }

    public function setErrorView($errorView) {
        $this->errorView = $errorView;
    }

    public function dispatchRequest($parameters = null) {
        $success = false;
        try {
            // Workaround for dot-to-underscore conversion of PHP script parameters
            foreach($_REQUEST as $key => $value) {
                if(Pageley_Util_String::contains($key, "_")) {
                    $_REQUEST[Pageley_Util_String::replace($key, "_", ".")] = $value;
                }
            }
            if($parameters === null) {
                $parameters = new Pageley_Util_ArrayMap($_REQUEST);
            }
            $controllerName = $parameters->get("controller");
            $this->initializeRequest();
            if($controllerName === null) {
                Pageley_Log::warn($this, "No controller specified in request.");
                $this->handleNotFound();
            }
            $modelAndView = null;
            foreach($this->filters as $regex => $filter) {
                $pattern = new Pageley_Util_Pattern("@" . $regex ."@");
                if(!$pattern->match($controllerName)->isEmpty()) {
                    $modelAndView = $filter->doFilter($parameters);
                    if($modelAndView !== null) {
                        break;
                    }
                }
            }
            if($modelAndView === null) {
                $controller = $this->controllers->get($controllerName);
                if($controller === null) {
                    Pageley_Log::warn($this, "Controller '" . $controllerName . "' is not defined.");
                    $this->handleNotFound();
                } else {
                    if(Pageley_Log::isDebugEnabled($this)) {
                        Pageley_Log::debug($this, "Dispatching to controller '" . $controllerName . "' for request.");
                    }
                    $modelAndView = $controller->handleRequest($parameters);
                }
            }
            $model = $modelAndView->getModel();
            $this->exposeModel($model);
            $view = null;
            if($parameters->containsKey("successView")) {
                $view = $parameters->get("successView");
            } else {
                $view = $modelAndView->getView();
            }
            if($view === null) {
                $this->errorHandler->handleError($model);
                $this->exposeModel($model);
                $this->resolveErrorView();
            } else {
                $this->resolveView($view);
                $modelAndView->doAfterViewAction();
                $success = true;
            }
        } catch(Exception $e) {
            $model = new Pageley_Util_ArrayMap();
            $model->put("pageley.exception", $e);
            Pageley_Log::error($this, "Error calling controller '" . $controllerName . "': " . $e);
            $this->errorHandler->handleError($model);
            $this->exposeModel($model);
            $this->resolveErrorView();
        }
        return $success;
    }
    
    private function handleNotFound() {
        $model = new Pageley_Util_ArrayMap();
        $this->notFoundHandler->handleError($model);
        $this->exposeModel($model);
        $this->resolveErrorView();
    }
    
    protected function getErrorView() {
        return $this->errorView;
    }
    
    public function dispatchPagelet($parameters) {
        $controllerName = $parameters->get("controller");
        if($controllerName === null) {
            throw new Pageley_Controller_Exception("No controller specified for pagelet.");
        }
        $controller = $this->controllers->get($controllerName);
        if($controller === null) {
            throw new Pageley_Controller_Exception("Controller '" . $controllerName . "' specified for pagelet is not defined.");
        } else {
            if(Pageley_Log::isDebugEnabled($this)) {
                Pageley_Log::debug($this, "Dispatching to controller '" . $controllerName . "' for pagelet.");
            }
            $modelAndView = $controller->handleRequest($parameters);
            $model = $modelAndView->getModel();
            $model->putAll($parameters);
            $this->exposeModel($model);
            $view = null;
            if($parameters->containsKey("successView")) {
                $view = $parameters->get("successView");
            } else {
                $view = $modelAndView->getView();
            }
            $this->viewResolver->resolveView($view);
        }
    }
    
    private function exposeModel($model) {
        if($model === null) {
            $model = new Pageley_Util_ArrayMap();
        }
        $model->put("pageley.application", $this->application);
        foreach($model as $key => $value) {
            $_REQUEST[$key] = $value;
        }
    }

    protected function initializeRequest() { }
    
    protected function resolveView($view) {
    	$this->getViewResolver()->resolveView($view);
    }
    
    protected function resolveErrorView() {
    	$this->getViewResolver()->resolveView($this->getErrorView());
    }
}